package com.centsuse.my_redis.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.centsuse.my_redis.dao.CallerToken;
import com.centsuse.my_redis.service.CallerTokenService;
import com.centsuse.my_redis.mapper.CallerTokenMapper;
import org.redisson.api.RBucket;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.util.concurrent.TimeUnit;


/**
* @author bobo
* @description 针对表【caller_token(业务方token表)】的数据库操作Service实现
* @createDate 2025-09-04 15:16:24
*/
@Service
public class CallerTokenServiceImpl extends ServiceImpl<CallerTokenMapper, CallerToken>
    implements CallerTokenService {
//
//    @Autowired
//    private CallerTokenRepository callerTokenRepository;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    @Lazy
    private CallerTokenServiceImpl callerTokenService;

//    @Override
//    @Cacheable(value = "callerToken", key = "#caller", unless = "#result == null", sync = true)
//    public CallerToken getCallerToken(String caller) {
//        // 使用 LambdaQueryWrapper 构建查询条件：查询 token 字段等于传入参数的记录
//        return callerTokenService.getCallerTokenInternal(caller);
//    }

    @Override
    public CallerToken insertCallerToken(CallerToken callerToken) {
        String lockKey = callerToken.getCaller();
        RLock rLock = redissonClient.getLock(lockKey);
        boolean acquired = false;
        try {
            acquired = rLock.tryLock(3000, 10000, TimeUnit.MILLISECONDS);
            if (acquired) {
//                callerTokenRepository.save(callerToken);
                return callerTokenService.insertCallerTokenInternal(callerToken);
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            if (null != rLock && rLock.isHeldByCurrentThread()) {
                rLock.unlock();
            }
        }

        return null;
    }

    @CachePut(value = "callerToken", key = "#callerToken.caller", condition = "#result != null")
    public CallerToken insertCallerTokenInternal(CallerToken callerToken) {
        if (this.save(callerToken)) {
            return callerToken;
        } else {
            return null;
        }
    }

    public CallerToken getCallerTokenInternal(String caller) {
        // 1. 构建缓存键（与@Cacheable的key表达式一致）
        String cacheKey = "callerToken:" + caller;

        // 1. 尝试从缓存获取：使用 RBucket 而不是 RMap
        RBucket<CallerToken> bucket = redissonClient.getBucket(cacheKey);
        CallerToken cachedToken = bucket.get();
        if (cachedToken != null) {
            // 缓存命中直接返回
            return cachedToken;
        }
        // 3. 构建分布式锁键（使用token作为锁粒度）
        String lockKey = caller;
        RLock lock = redissonClient.getLock(lockKey);
        try {
            // 4. 尝试获取锁（实现sync=true的同步加载）
            boolean locked = lock.tryLock(2, 10, TimeUnit.SECONDS);
            if (!locked) {
                // 获取锁失败，可重试或抛出异常
                throw new RuntimeException("Unable to acquire lock for cache loading");
            }
            try {
                // 5. 双重检查（获取锁后再次检查缓存）
                cachedToken = bucket.get();
                if (cachedToken != null) {
                    return cachedToken;
                }

                // 6. 执行数据库查询（替换原注解中的方法执行）
                CallerToken dbToken = this.getOne(new LambdaQueryWrapper<CallerToken>()
                                // 添加等值查询条件[6,7,8](@ref)
                                .eq(CallerToken::getCaller, caller)
                        // 第二个参数 false 表示即使找到多条记录也不抛出异常（但唯一索引理论上不会出现多条）
                        , false);

                // 7. 处理查询结果（实现unless="#result == null"）
                if (dbToken != null) {
                    // 8. 写入缓存（实现@Cacheable的缓存写入）
                    // 设置TTL
                    bucket.set(dbToken, Duration.ofMinutes(60));
                }
                return dbToken;
            } finally {
                lock.unlock();
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("Cache loading interrupted", e);
        }
    }

    @Override
    public CallerToken getCallerToken(String caller) {
        // 使用 LambdaQueryWrapper 构建查询条件：查询 token 字段等于传入参数的记录
        return this.getCallerTokenInternal(caller);
    }

    @Override
    @Cacheable(value = "callerToken", key = "#caller", unless = "#result == null")
    public CallerToken getCallerTokenByCache(String caller) {
        try {
            // 使用LambdaQueryWrapper避免字段名硬编码，提高类型安全[4](@ref)
            LambdaQueryWrapper<CallerToken> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            // 假设实体类中getter方法为getCallerToken
            lambdaQueryWrapper.eq(CallerToken::getCaller, caller)
                    .last("LIMIT 1");
            // 执行查询
            return this.getOne(lambdaQueryWrapper);
        } catch (Exception e) {
            // 日志记录异常信息，便于排查问题（实际使用时注入日志工具，如@Slf4j）
            // log.error("根据token查询CallerToken时发生异常: token = {}", token, e);
            return null;
        }
    }


}




